package com.ponxu.web;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ponxu.log.Log;
import com.ponxu.log.LogFactory;
import com.ponxu.mb.web.filter.URLMappingFilter;
import com.ponxu.utils.Lang;
import com.ponxu.web.core.Action;
import com.ponxu.web.core.ResultRender;

/**
 * 流转控制中心<br>
 * 
 * 1 URL Rewrite<br>
 * 2 Dispather<br>
 * 3 参数封装<br>
 * 
 * @author xwz
 * 
 */
@SuppressWarnings("unchecked")
public class Controller implements Filter {
	private static final Log LOG = LogFactory.getLog();

	/** 结果处理 */
	private String renderClassName;
	private ResultRender render;

	/** Dispather参数 */
	private String operation;

	/** action所在包 */
	private String actionPackage;

	/** Action类名前缀 */
	private String actionClassPrefix;
	/** Action类名后缀 */
	private String actionClassSuffix;

	/** rewrite映射 */
	private String rewriteMapping;
	private Map<Pattern, String> urlPattern;

	/** 不关心 */
	private String ignorePrefix;
	private String ignoreSuffix;
	private String[] ignorePrefixs;
	private String[] ignoreSuffixs;

	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		LOG.debug("----------Controller begin----------");

		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;

		boolean processed = false;
		String uri = request.getServletPath();
		LOG.debug("Controller uri: %s", uri);

		// 1 是否忽略前后缀
		if (!ignore(uri)) {
			// 2 处理URL重写
			processed = urlRewrite(request, response);

			// 3.1未处理URL重写
			if (!processed) {
				// 3.2 Action处理
				processed = dispatch(request, response);
			}
		}

		// 若未进行处理, 继续流转
		if (!processed) {
			chain.doFilter(request, response);
		}
		LOG.debug("----------Controller end----------");
	}

	// 过滤器是否忽略
	private boolean ignore(String uri) {
		if (ignorePrefixs != null) {
			for (String prefix : ignorePrefixs) {
				if (uri.startsWith(prefix)) {
					LOG.debug("ignore prefix: %s", prefix);
					return true;
				}
			}
		}

		if (ignoreSuffixs != null) {
			for (String suffix : ignoreSuffixs) {
				if (uri.endsWith(suffix)) {
					LOG.debug("ignore suffix: %s", suffix);
					return true;
				}
			}
		}

		return false;
	}

	// Action处理
	private boolean dispatch(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		boolean processed = false;
		String uri = request.getServletPath();
		if (Lang.isEmpty(uri))
			return processed;

		String actionClassName = getActionClassName(uri);
		String methodName = request.getParameter(operation);

		Action action = findAction(actionClassName);
		if (action != null) {
			// 进行action处理
			LOG.debug("action=%s, method=%s", actionClassName,
					Lang.defaultString(methodName, "execute"));

			// action调用之前, 结果影响是否继续
			boolean continueGo = action.before(request, response);
			boolean findAMethod = true;

			if (continueGo) {
				try {
					// action的执行结果
					String result = invokeAction(action, methodName, request,
							response);
					// action执行之后, 可继续影响执行结果
					result = action.after(request, response, result);
					if (render != null) {
						render.before(request, response, result);
						render.render(request, response, result);
						render.after(request, response, result);
					}
				} catch (SecurityException e) {
					findAMethod = false;
				} catch (NoSuchMethodException e) {
					findAMethod = false;
				}
			}
			processed = findAMethod;
		}

		return processed;
	}

	// Action单实例缓存
	private Map<String, Action> actionCache = new HashMap<String, Action>();

	// 获取action实例
	private Action findAction(String actionClassName) {
		Action action = actionCache.get(actionClassName);
		if (action == null) {
			action = tryNewInstance(actionClassName, Action.class);
			if (action != null) {
				actionCache.put(actionClassName, action);
			}
		}
		return action;
	}

	// Method缓存
	private Map<String, Method> methodCache = new HashMap<String, Method>();

	// 获取Action待执行方法
	private Method findActionMehtod(Action action, String methodName)
			throws SecurityException, NoSuchMethodException {
		Class<Action> actionClass = (Class<Action>) action.getClass();

		String key = actionClass.getName() + "!" + methodName;
		Method m = methodCache.get(key);
		if (m == null) {
			m = actionClass.getDeclaredMethod(methodName,
					HttpServletRequest.class, HttpServletResponse.class);
			if (m != null) {
				methodCache.put(key, m);
			}
		}

		return m;
	}

	// action执行
	private String invokeAction(Action action, String methodName,
			HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException, SecurityException,
			NoSuchMethodException {
		String result = null;
		if (Lang.isEmpty(methodName)) {
			// 在未指定方法的情况下, 调用默认方法
			result = action.execute(request, response);
		} else {
			// 调用action中指定的方法
			Method actionMethod = findActionMehtod(action, methodName);
			if (actionMethod != null) {
				try {
					result = (String) actionMethod.invoke(action, request,
							response);
				} catch (IllegalArgumentException e) {
					Lang.makeThrow(e);
				} catch (IllegalAccessException e) {
					Lang.makeThrow(e);
				} catch (InvocationTargetException e) {
					Lang.makeThrow(e);
				}
			}
		}
		return result;
	}

	// 解析Action class
	private String getActionClassName(String uri) {
		int lastSlashIndex = uri.lastIndexOf("/");

		String action = uri.substring(lastSlashIndex + 1);
		String pkg = uri.substring(0, lastSlashIndex);

		StringBuilder fullClassName = new StringBuilder(actionPackage);
		fullClassName.append(pkg.replace("/", ".")).append(".")
				.append(actionClassPrefix).append(Lang.firstUpperCase(action))
				.append(actionClassSuffix);
		return fullClassName.toString();
	}

	// 处理URL重写
	private boolean urlRewrite(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		boolean processed = false;
		String uri = request.getServletPath();
		if (Lang.isEmpty(uri))
			return processed;
		request.setAttribute("OldURI", uri);

		Iterator<Entry<Pattern, String>> it = urlPattern.entrySet().iterator();
		while (it.hasNext()) {
			Entry<Pattern, String> entry = it.next();
			Pattern key = entry.getKey();
			String value = entry.getValue();

			Matcher matcher = key.matcher(uri);
			if (matcher.find()) {
				processed = true;
				int c = matcher.groupCount();
				for (int i = 1; i <= c; i++) {
					value = value.replace("{" + i + "}", matcher.group(i));
				}
				LOG.debug("rewrite: %s", value);
				request.getRequestDispatcher(value).forward(request, response);
				break;
			}
		}
		return processed;
	}

	// Filter初始化
	public void init(FilterConfig fConfig) throws ServletException {
		// 结果渲染
		renderClassName = Lang.defaultString(
				fConfig.getInitParameter("render"),
				"com.ponxu.web.core.JSPResultRender");

		// 分发参数
		operation = Lang.defaultString(fConfig.getInitParameter("operation"),
				"oper");

		// action所在包
		actionPackage = Lang.defaultString(fConfig
				.getInitParameter("actionPackage"));

		// action类前后缀
		actionClassPrefix = Lang.defaultString(fConfig
				.getInitParameter("actionClassPrefix"));
		actionClassSuffix = Lang.defaultString(fConfig
				.getInitParameter("actionClassSuffix"));

		// URL重写策略文件
		rewriteMapping = Lang.defaultString(
				fConfig.getInitParameter("rewriteMapping"), "/urls.properties");

		// 过滤器忽略配置
		ignorePrefix = fConfig.getInitParameter("ignorePrefix");
		ignoreSuffix = fConfig.getInitParameter("ignoreSuffix");

		loadRender(fConfig.getServletContext());
		loadRewrite();
		loadIgnore();
	}

	public void destroy() {
	}

	// 加载忽略配置
	private void loadIgnore() {
		LOG.debug("ignorePrefix=%s", ignorePrefix);
		LOG.debug("ignoreSuffix=%s", ignoreSuffix);

		if (Lang.isNotEmpty(ignorePrefix)) {
			ignorePrefixs = ignorePrefix.split(",");
			trimStringArray(ignorePrefixs);
		}

		if (Lang.isNotEmpty(ignoreSuffix)) {
			ignoreSuffixs = ignoreSuffix.split(",");
			trimStringArray(ignoreSuffixs);
		}

		ignorePrefix = null;
		ignoreSuffix = null;
	}

	private void trimStringArray(String[] arr) {
		for (int i = 0; arr != null && i < arr.length; i++) {
			arr[i] = arr[i].trim();
		}
	}

	// 加载rewrite配置
	private void loadRewrite() {
		urlPattern = new LinkedHashMap<Pattern, String>();
		InputStream in = URLMappingFilter.class
				.getResourceAsStream(rewriteMapping);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));
		try {
			String line = null;
			while ((line = br.readLine()) != null) {
				int i = line.indexOf("=");
				if (!line.trim().startsWith("#") && i > 0) {
					String key = line.substring(0, i);
					String value = line.substring(i + 1);
					LOG.debug("rewrite %s=%s", key, value);
					Pattern p = Pattern.compile(key);
					urlPattern.put(p, value);
				}
			}

			br.close();
			in.close();

			in = null;
			br = null;
			rewriteMapping = null;
		} catch (IOException e) {
			LOG.error(rewriteMapping, e);
		}
	}

	// 加载结果渲染器
	private void loadRender(ServletContext app) {
		LOG.debug("renderClassName=%s", renderClassName);
		render = tryNewInstance(renderClassName, ResultRender.class);
		if (render != null) {
			render.init(app);
		}
		renderClassName = null;
	}

	// 新建实例
	private <T> T tryNewInstance(String className, Class<T> clzz) {
		try {
			Class<?> cls = Class.forName(className);
			if (clzz.isAssignableFrom(cls)) {
				return (T) cls.newInstance();
			}
		} catch (ClassNotFoundException e) {
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		return null;
	}
}
